package cn.nawang.ebeim.client.transfer;

import cn.nawang.ebeim.client.process.ProcessBar;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.net.URISyntaxException;

import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;

public class TransferUploadHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final Logger LOG = LoggerFactory.getLogger(TransferUploadHandler.class);

    private TransferObject obj;

    private Long preLength = 0l;

    private Long preUploadLength = 0l;

    private String errorMsg = "" ;

    private boolean isConnect ;

    private boolean isSuccess;

    private TransferCallback callback ;

    // 定义客户端没有收到服务端的pong消息的最大次数
    private static final int MAX_PONG_TIMES = 20;

    private int pongTimes = 0;

    public TransferUploadHandler(TransferObject obj, TransferCallback callback,long preUploadLength) {
        this.obj = obj;
        this.callback = callback ;
        this.preUploadLength = preUploadLength ;
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.WRITER_IDLE) {
                if (pongTimes >= MAX_PONG_TIMES) {
                    LOG.info("===客户端===(WRITER_IDLE 写超时)" + pongTimes);
                    isSuccess = false;
                    ctx.close();
                } else {
                    LOG.info("Got ping response for server:{},{}" ,ctx.channel().remoteAddress(), event.state());
                    pongTimes++;
                }
            }
        }
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        LOG.info("channelRegistered:{}",obj);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        LOG.info("channelUnregistered:{}",obj);
        if(!isConnect){
            callback.onTransferFailed(obj,new TransferException("网络信号差,无法建立连接!"));
            return;
        }
        if(isSuccess){
            callback.onTransferSuccess(obj);
        }else {
            callback.onTransferFailed(obj,new TransferException("文件上传失败!" + errorMsg));
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        isConnect = true ;
        upload(ctx);
    }

    private void upload(ChannelHandlerContext ctx) throws Exception {
        File file = new File(obj.getFilePath());
        QueryStringEncoder encoder = new QueryStringEncoder("upload");
        encoder.addParam("dataId", obj.getDsId());
        encoder.addParam("signature", obj.getSignature());
        if(preUploadLength>0){
            encoder.addParam("isBreak", "true");
        }else {
            encoder.addParam("isBreak", "false");
        }
        URI uriGet = new URI(encoder.toString());
        String uri = TransferAuthService.encryptUrl(uriGet.toASCIIString());
        HttpRequest request =
                new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        HttpHeaders.setContentLength(request, file.length());

        MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
        request.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
        if (HttpHeaders.isKeepAlive(request)) {
            request.headers().set("CONNECTION", HttpHeaders.Values.KEEP_ALIVE);
        }

        ctx.write(request);
        uploadFile(ctx,preUploadLength);
    }

    private void uploadFile(ChannelHandlerContext ctx, Long uploadLength) throws IOException, URISyntaxException {
        File file = new File(obj.getFilePath());
        if (!file.exists()) {
            LOG.info("不存在文件：" + file.getAbsolutePath());
            ctx.close();
        } else {
            final RandomAccessFile raf = new RandomAccessFile(file, "r");
            long fileLength = file.length();
            if(uploadLength>0){
                if(uploadLength>fileLength){
                    uploadLength = 0l ;
                }
                fileLength = fileLength - uploadLength;
                LOG.info("file already exist in server, upload from index: " + uploadLength + ",total transfer:" + fileLength);
            }else {
                LOG.info("upload start ==>  {}", obj);
            }
            ChannelFuture sendFileFuture =
                    ctx.write(new DefaultFileRegion(raf.getChannel(), uploadLength, fileLength),
                    ctx.newProgressivePromise());
            sendFileFuture.addListener(new ChannelProgressiveFutureListener() {

                public void operationProgressed(ChannelProgressiveFuture future, long progress,long total) {
                    pongTimes = 0 ;
                    LOG.debug("progress: " + progress + "/" + total);

                    if(obj.getProcessBar()!=null){
                        obj.getProcessBar().increaseRate(progress - preLength);
                        preLength = progress;
                    }

                }


                public void operationComplete(ChannelProgressiveFuture future) throws Exception {
                    raf.close();
                    if (future.isSuccess()) {
                        LOG.info("数据传输完成:{} " , obj);
                    } else {
                        LOG.info("传输到服务端失败:{}" , future.cause());
                    }
                }
            });
            ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if (msg instanceof HttpResponse) {
            HttpResponse response = (HttpResponse) msg;
            if (HttpResponseStatus.OK.equals(response.getStatus())) {
                isSuccess = true ;
                LOG.info("upload success: " + obj);
            } else {
                isSuccess = false ;
                errorMsg = "upload failed: " + obj + ", errorMsg: " + response.headers().get("error");
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        errorMsg =cause.getMessage();
        ctx.close();
    }

}
